﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Lacrima.Framework.Infrastructure
{
    public abstract class Resolver<TEntity, TElement> : IResolver
        where TElement : class
        where TEntity : class
    {
        public abstract bool ValueEquals(TEntity source, TElement destination);

        public virtual TResult ResolveValue<TSource, TResult>(TSource source, Expression<Func<TSource, TResult>> expression)
            where TSource : class
        {
            return ResolvePropertyValue(source, expression);
        }

        /// <summary>
        /// 判断两个类中的字段是否是匹配的，即映射中的字段值是否相等
        /// </summary>
        /// <typeparam name="TSource">The type of the t source.</typeparam>
        /// <typeparam name="TSourceMember">The type of the t source member.</typeparam>
        /// <typeparam name="TDestination">The type of the t destination.</typeparam>
        /// <typeparam name="TDestinationMember">The type of the t destination member.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="sourceExpression">The source expression.</param>
        /// <param name="destination">The destination.</param>
        /// <param name="destinationExpression">The destination expression.</param>
        /// <returns><c>true</c> if the specified source is matched; otherwise, <c>false</c>.</returns>
        /// <exception cref="System.IndexOutOfRangeException"></exception>
        public virtual bool IsMatched<TSource, TSourceMember, TDestination, TDestinationMember>
                                     (TSource source, Expression<Func<TSource, TSourceMember>> sourceExpression,
                                       TDestination destination, Expression<Func<TDestination, TDestinationMember>> destinationExpression)
            where TSource : class
            where TDestination : class
        {
            TSourceMember sourceValue = ResolveValue(source, sourceExpression);
            TDestinationMember destinationValue = ResolveValue(destination, destinationExpression);
            bool result = ResolveEquals(sourceValue, destinationValue);
#if DEBUG
            Debug.WriteLine(string.Format("The result is： {0}", result));
            Debug.WriteLine(string.Format("The source value is： {0}", sourceValue == null ? "null" : sourceValue.ToString()));
            Debug.WriteLine(string.Format("The destination value is： {0}", destinationValue == null ? "null" : destinationValue.ToString()));
            if (result == false)
                throw new IndexOutOfRangeException();
#endif
            return result;
        }

        /// <summary>
        /// 解析两个有关联关系的类中的映射字段值是否相等
        /// </summary>
        /// <typeparam name="TSource">The type of the t source.</typeparam>
        /// <typeparam name="TDestination">The type of the t destination.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="destination">The destination.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        public virtual bool ResolveEquals<TSource, TDestination>(TSource source, TDestination destination)
        {
            bool result = false;
            if (typeof(TSource).IsEquivalentTo(typeof(int)) && typeof(TDestination).IsEnum)
            {
                result = Enum.Parse(typeof(TDestination), source.ToString()).Equals(destination);
            }
            else if (typeof(TDestination).IsEquivalentTo(typeof(int)) && typeof(TSource).IsEnum)
            {
                result = Enum.Parse(typeof(TSource), destination.ToString()).Equals(source);
            }
            else if (typeof(TSource).IsEquivalentTo(typeof(TDestination)))
            {
                if (typeof(TSource).IsClass)
                {
                    if (source == null || destination == null)
                    {
                        result = (source == null && destination == null);
                    }
                    else
                    {
                        result = source.Equals(destination);
                    }
                }
                else
                {
                    // double 类型做经度处理
                    result = source.Equals(destination);
                }
            }
            return result;
        }

        /// <summary>
        /// 解析类中属性值
        /// </summary>
        /// <typeparam name="TSource">The type of the t source.</typeparam>
        /// <typeparam name="TSourceMember">The type of the t source member.</typeparam>
        /// <param name="source">The source.</param>
        /// <param name="expression">The expression.</param>
        /// <returns>``1.</returns>
        public static TSourceMember ResolvePropertyValue<TSource, TSourceMember>(TSource source, Expression<Func<TSource, TSourceMember>> expression) where TSource : class
        {
            PropertyInfo property = GetPropertyInfo(source, expression);
            var value = GetPropertyValue(source, expression);

            if (value != null)
            {
                return (TSourceMember)property.GetValue(value);
            }
            else
            {
                return (TSourceMember)property.GetValue(source); ;
            }
        }

        private static PropertyInfo GetPropertyInfo<TSource, TProperty>(TSource source, Expression<Func<TSource, TProperty>> expression) where TSource : class
        {
            Type type = typeof(TSource);
            MemberExpression member = expression.Body as MemberExpression;
            if (member == null)
                throw new ArgumentException(string.Format(
                    "Expression '{0}' refers to a method, not a property.",
                    expression.ToString()));

            PropertyInfo property = member.Member as PropertyInfo;
            if (property == null)
                throw new ArgumentException(string.Format(
                    "Expression '{0}' refers to a field, not a property.",
                    expression.ToString()));

#if DEBUG
            Debug.WriteLine(string.Format("Compared property name is： {0}", property.Name));
            Debug.WriteLine(string.Format("Compared property type is： {0}", property.PropertyType.Name));
#endif

            return property;
        }

        private static object GetPropertyValue<TSource, TProperty>(TSource source, Expression<Func<TSource, TProperty>> expression) where TSource : class
        {
            string[] prepArray = expression.Body.ToString().Split('.');
            object target = source;
            object result = null;

            for (int index = 1; target != null && index < prepArray.Length - 1; ++index)
            {
                PropertyInfo[] properties = target.GetType().GetProperties();
                var prep = properties.FirstOrDefault(o => o.Name.Equals(prepArray[index]));
                result = prep.GetValue(target);
                target = result;
            }

            return result;
        }
    }
}
